# -*-coding:utf-8 -*-
#
# Created on 2019/5/13, by felix
#

from rest_framework import serializers, exceptions
from rest_framework.validators import UniqueTogetherValidator
from rest_framework.validators import UniqueValidator

from .models import CommandInfo, Owner


# 110 序列化-序列化器
class CommandInfoSerializer(serializers.ModelSerializer):
    # 111 数据库字段属性 - 数据库存在的
    name = serializers.CharField()
    desc = serializers.CharField()
    version = serializers.CharField()
    sex = serializers.IntegerField()

    # 112 自定义序列化属性 - 数据库可以不存在
    """
    使用场景：choices
            # choice类型的解释型值， get_字段_display()来访问

    属性名随意，值由固定的命名规范方法提供：
    def get_属性名(self, 参与序列化的model对象)
         return 自定义序列化属性的值
    """
    gender = serializers.SerializerMethodField()  # 数据库不存在的， 自定义的序列化属性的method
    version_desc = serializers.SerializerMethodField()

    def get_gender(self, obj):
        # print(obj, type(obj))
        return obj.get_sex_display()

    def get_version_desc(self, obj):
        version = obj.version
        desc = obj.desc
        # print(version)
        return "%s-%s" % (desc, version)

    class Meta:
        model = CommandInfo
        # fields = '__all__'  # 全部字段
        fields = ('id', 'name', 'desc', 'version', 'created_by', 'sex', 'gender', 'version_desc')
        read_only_fields = ('created_time', 'updated_time')


# 120 反序列化-验证器
class CommandInfoCreateSerializer(serializers.ModelSerializer):
    # 121 UniqueValidator：单一字段unique
    name = serializers.CharField(validators=[UniqueValidator(queryset=CommandInfo.objects.all(), message="name不能重复")])

    # 122 require，blank，null的关系，哪怕是在model定义了为空，依旧可以在serial设置不能为空
    desc = serializers.CharField(required=True)  # null=True,blank=True
    version = serializers.CharField(required=True)

    # 123 验证字段，以G开头
    def validate_desc(self, desc):
        if not desc.startswith('G'):
            raise serializers.ValidationError('desc必须是以G开头的')
        return desc

    # 124 多个字段验证
    def validate(self, attrs):
        # attr: OrderedDict([('name', 'll -a -t'), ('desc', 'Gdafa'), ('version', 'dad')])
        version = attrs.get('version')
        if not version.startswith('V'):
            raise serializers.ValidationError('Version必须为V开头的')

        created_by = attrs.get('created_by')
        if not created_by:
            raise serializers.ValidationError('created_by不能为空')

        sex = attrs.get('sex')
        if sex not in [1, 2, 3]:
            raise serializers.ValidationError('sex必须为1,2,3')

        return attrs

    class Meta:
        model = CommandInfo
        fields = ('id', 'name', 'desc', 'version', 'created_by', 'sex')
        read_only_fields = ('created_time', 'updated_time')

        # 125 UniqueTogetherValidator 联合字段unique：name和desc
        validators = [UniqueTogetherValidator(queryset=CommandInfo.objects.all(), fields=['name', 'desc'],
                                              message="name和desc该条记录已经存在")]


# 210 序列化器
# 序列化提供给前台的字段个数由后台决定，可以少提供，
# 但是提供的数据库对应的字段，名字一定要与数据库字段相同
class OwnerSerializer(serializers.ModelSerializer):
    class Meta:
        model = Owner
        fields = ['id', 'name', 'nickname', 'sex', 'pwd', 'phone', 'weixin', 'work_email', 'email', 'created_time',
                  'updated_time']
        read_only_fields = ('created_time', 'updated_time')


# 220 验证器
# 1. 哪些字段必须反序列化
# 2. 字段有哪些安全校验
# 3. 哪些字段需要额外提供校验
# 4. 哪些字段间存在联合校验
# 注：反序列化字段都是用来入库的，不会出现自定义方法属性，会出现可以设置校验规则的自定义属性re_pwd
class OwnerCreateSerializer(serializers.ModelSerializer):
    # 221 系统校验字段
    # name、email、pwd、re_pwd为必填字段，必填字段需要局部钩子
    # weixin为非必填字段
    weixin = serializers.CharField(required=False)  # # model定义为null=True, blank=True
    email = serializers.EmailField(required=True)
    name = serializers.CharField(
        max_length=64, min_length=3,
        error_messages={'max_length': "name必须少于64个字符",
                        'min_length': 'name必须大于3个字符'}
    )

    # 222 自定义校验
    # 自定义有校验规则的反序列化字段，不能入库
    re_pwd = serializers.CharField(required=True)

    # 222 局部钩子：valiade_要校验的字段名(self,当前要校验字段的值)
    # 校验规则：校验通过返回原值，校验失败则抛出异常
    def validate_name(self, value):
        print('局部钩子')
        print(value)
        if 'j' in value.lower():
            raise serializers.ValidationError('名字中不能包含j')  # 推荐用法
            # raise exceptions.ValidationError('名字中不能包含j')  # 实质是exception的非法验证错误
        return value
        # return super().validate_empty_values(data)   # 不重写了，直接调用父的方法

    # 223 全局钩子：valiad(self,系统与局部钩子校验通过的所有数据)
    # 校验规则：校验通过返回原值，校验失败则抛出异常
    def validate(self, attrs):
        print('全局钩子')
        print(attrs)

        # 自定义的字段，如何保存create
        pwd = attrs.get('pwd')
        re_pwd = attrs.pop('re_pwd')  # 不会入库，要删除 pop ！！！
        if pwd != re_pwd:
            raise serializers.ValidationError({"pwd": '两次密码不一致'})  # 定义返回的erro的key-value
            # raise serializers.ValidationError('两次密码不一致')

        return attrs

    # 224 要完成新增，需要自己重写create方法
    # TypeError: Owner() got an unexpected keyword argument 're_pwd'
    def create(self, validated_data):
        print('重新create方法')
        print(validated_data)
        # 尽量在所有校验规则完毕之后，数据可以直接入库
        obj = Owner.objects.create(**validated_data)
        return obj

    class Meta:
        model = Owner
        fields = ['id', 'name', 'nickname', 'sex', 'pwd', 'phone', 'weixin', 'work_email', 'email', 'created_time',
                  'updated_time', 're_pwd']
        read_only_fields = ('created_time', 'updated_time')

    # 总结
    # 1) 设置必填和选填序列化字段，设置校验规则
    # 2) 为需要额外校验的字段提供局部钩子函数，如果某些字段不入库，且不参与全局钩子校验，可以将其取出校验
    # 3) 为有联合关系的字段们提供全局钩子函数，如果某些字段不入库, 必须pop值取出校验
    # 4) 重写create方法，完成校验通过的数据，入库工作，得到新增的对象(某些字段需要pop)


'''



      def to_representation(self, obj):
      """serializer转化展示给前端的json"""
      # print(1111111111111)
      # print(obj)
      # print(type(obj))
      print(obj.message)
      print(type(obj.message))

      # t1 = json.loads(obj.message)
      # print(type(t1))
      ret = None
      try:
          ret = json.loads(obj.message)
      except ValueError as e:
          ret = obj.message
      print(ret)
      print(type(ret))
      data = {"id": obj.id,
              "message": ret,
              # "time_stamp": obj.time_stamp,
              "log_type": obj.log_type}
      # return super().to_representation(obj)
      return data

  '''
